package com.hrt.datasender.datacollect.drive.weigeng;

import java.text.NumberFormat;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.junit.Test;

import com.btcode.common.MyLog;
import com.btcode.log.ILog;

/**
 * 
 * 微耕报文，粘包拆包处理
 * 
 * 微耕报文的理解 <br>
 * 17 20 00 00 1D 85 B5 0D 04 00 00 00 01 00 01 01 0D D7 37 00 20 15 04 29 16 37
 * 39 06 01 01 00 00 00 00 00 00 00 16 42 25 00 00 00 00 00 00 00 00 00 00 00 15
 * 04 29 00 00 00 00 00 00 00 00 00 00 <br>
 * <br>
 * 两个数字是一个字节，一个报文总共有64个字节, 一个字节代表8位，即1byte = 8bit，所以四字节就是32位，下面进制转换会用到 <br>
 * <strong></string>,<b>0 ~ 3，共四个字节，17 20 00 00，是指令部分</b>
 * <ol>
 * <li>第一个字节17，是报文的开头</li>
 * <li>第二个字节20,可以认为是功能码，在这里，20表示查询控制器状态的意思</li>
 * </ol>
 * <br>
 * <strong>controlNo</string><b>4 ~ 7，共四个字节，1D 85 B5 0D，是设备序列号</b>
 * <ol>
 * <li>设备序列号是高低位互换的16进制，解析的时候，应该换成：0D B5 85 1D,转换成10进制就是229999901</li>
 * </ol>
 * <br>
 * <strong>recordNoByte</string><b>8 ~ 11，共四个字节，04 00 00 00，是最后一条记录的索引号</b>
 * <ol>
 * <li>也是高低位互换，结果：00 00 00 04，也就是4</li>
 * </ol>
 * <br>
 * <strong>recordTypeByte</string> <b>12，共一个字节，01，表示记录类型</b>
 * <ul>
 * <li>0:无记录</li>
 * <li>1:刷卡记录</li>
 * <li>2:门磁,按钮, 设备启动, 远程开门记录</li>
 * <li>3:警记录</li>
 * </ul>
 * <strong>recordResultByte</string><b>13，共一个字节，00，表示有效性</b>
 * <ul>
 * <li>0:不通过</li>
 * <li>1:通过</li>
 * </ul>
 * <strong>doorNoByte</string> <b>14，共一个字节，01，表示门号</b> <br>
 * <strong>inOrOutByte</string><b>15，共一个字节，01，表示进出门</b>
 * <ul>
 * <li>1:进门</li>
 * <li>2:出门</li>
 * </ul>
 * <strong>carNoByte</string><b>16 ~ 19，共四个字节，0D D7 37 00，表示卡号</b>
 * <ol>
 * <li>也是高低位互换</li>
 * </ol>
 * <strong>recordDateTimeByte</string><b>20 ~ 26，共7个字节，20 15 04 29 16 37
 * 39，表示刷卡时间</b>
 * <ol>
 * <li>采用BCD编码，年存成两个字节，其余月日时分秒各一个字节</li>
 * <li>解析的时候，直接把这个字节转成16进制的字符即可</li>
 * <li>比如说年的第一个部分，这个20其实是16进制，在java中拿到的是byte，直接是十进制，那么要转成16进制的字符串，拿到数据部分即可</li>
 * </ol>
 * <strong>recordReasonByte</string><b>27，共1个字节，06，记录处理原因，对照关系，具体看文档，下面举例说明</b>
 * <ul>
 * <li>1：表示刷卡开门</li>
 * <li>5：表示电脑控制</li>
 * <li>6：表示没有权限</li>
 * <li>7：表示密码不对</li>
 * <li>……</li>
 * </ul>
 * <strong>doorMagnetismByte</string><b>28、29、30、31，每个共一个字节，01，表示门磁打开</b>
 * <ol>
 * <li>28:一号门磁</li>
 * <li>29:二号门磁</li>
 * <li>30:三号门磁</li>
 * <li>31:四号门磁</li>
 * </ol>
 * <ul>
 * <li>00:关上</li>
 * <li>01:打开</li>
 * </ul>
 * 
 * @author gislin
 *
 */
public class WeiGengMsgHandle extends SimpleChannelUpstreamHandler {
    
    private final int doorNo3 = 3;
    
    private final int doorNo4 = 4;
    
    private final int msgPrefixByteLength = 4;
    
    private final int controlNoByteLength = 4;
    
    private final int recordNoByteLength = 4;
    
    private final int carNoByteLength = 4;
    
    private final int recordDateTimeByteLength = 7;
    
    private ILog log;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent msgEvent) throws Exception {

        if (log == null) {
            log = MyLog.getInstance().getLogger(getClass());
        }

        super.messageReceived(ctx, msgEvent);
        Object obj = msgEvent.getMessage();
        byte[] source = (byte[]) obj;

        BdDoorAccessModel model = decode(source);

        log.info("收到门禁数据：" + model);

        boolean result = BdDoorAccessService.getInstance().create(model);

        if (result) {
            log.info("门禁数据入库成功");
        }
    }

    private BdDoorAccessModel decode(byte[] source) {

        NumberFormat numFormat = NumberFormat.getInstance();
        numFormat.setGroupingUsed(false);

        byte[] controlNoByte = new byte[controlNoByteLength];// 门禁序列号
        byte[] recordNoByte = new byte[recordNoByteLength];// 卡号
        byte[] recordTypeByte = new byte[1];// 记录类型
        byte[] recordResultByte = new byte[1];// 记录结果
        byte[] doorNoByte = new byte[1];// 门号
        byte[] inOrOutByte = new byte[1];// 进门或出门
        byte[] carNoByte = new byte[carNoByteLength];// 卡号
        byte[] recordDateTimeByte = new byte[recordDateTimeByteLength];// 记录时间
        byte[] recordReasonByte = new byte[1];// 记录原因
        byte[] doorMagnetismByte = new byte[1];// 门磁状态
        
        int srcPos = msgPrefixByteLength;
        System.arraycopy(source, srcPos, controlNoByte, 0, controlNoByteLength);
        srcPos += controlNoByteLength;
        System.arraycopy(source, srcPos, recordNoByte, 0, recordNoByteLength);
        srcPos += recordNoByteLength;
        System.arraycopy(source, srcPos, recordTypeByte, 0, 1);
        srcPos += 1;
        System.arraycopy(source, srcPos, recordResultByte, 0, 1);
        srcPos += 1; 
        System.arraycopy(source, srcPos, doorNoByte, 0, 1);
        srcPos += 1; 
        System.arraycopy(source, srcPos, inOrOutByte, 0, 1);
        srcPos += 1; 
        System.arraycopy(source, srcPos, carNoByte, 0, carNoByteLength);
        srcPos += carNoByteLength;
        System.arraycopy(source, srcPos, recordDateTimeByte, 0, recordDateTimeByteLength);
        srcPos += recordDateTimeByteLength;
        System.arraycopy(source, srcPos, recordReasonByte, 0, 1);
        // 根据门号找到所对应的门磁状态
        if (doorNoByte[0] == 1) {
            srcPos += 1;
            System.arraycopy(source, srcPos, doorMagnetismByte, 0, 1);
        }
        else if (doorNoByte[0] == 2) {
            srcPos += 2;
            System.arraycopy(source, srcPos, doorMagnetismByte, 0, 1);
        } 
        else if (doorNoByte[0] == doorNo3) {
            srcPos += doorNo3;
            System.arraycopy(source, srcPos, doorMagnetismByte, 0, 1);
        } 
        else if (doorNoByte[0] == doorNo4) {
            srcPos += doorNo4;
            System.arraycopy(source, srcPos, doorMagnetismByte, 0, 1);
        }
        String controlNo = numFormat.format(converToDouble(controlNoByte)) + "";
        String recordNo = numFormat.format(converToDouble(recordNoByte)) + "";
        String recordType = recordTypeByte[0] + "";
        String recordResult = recordResultByte[0] + "";
        String doorNo = doorNoByte[0] + "";
        String inOrOut = inOrOutByte[0] + "";
        String carNo = numFormat.format(converToDouble(carNoByte)) + "";
        String recordDateTime = bytesToBCDTimeString(recordDateTimeByte);
        String recordReason = recordReasonByte[0] + "";
        String doorMagnetism = doorMagnetismByte[0] + "";

        BdDoorAccessModel model = new BdDoorAccessModel();
        model.setCarNo(carNo);
        model.setControlNo(controlNo);
        model.setDoorNo(doorNo);
        model.setInOrOut(inOrOut);
        model.setRecordDatetime(recordDateTime);
        model.setRecordNo(recordNo);
        model.setRecordReason(recordReason);
        model.setRecordResult(recordResult);
        model.setRecordType(recordType);
        model.setdoorMagnetism(doorMagnetism);

        return model;
    }

    private double converToDouble(byte[] data) {
        final int dataByteAddr = 3;
        double value = convertByteToDouble32(data[0], data[1], data[2], data[dataByteAddr]);
        return value;
    }

    private double convertByteToDouble32(byte byte1, byte byte2, byte byte3, byte byte4) {
        final int maskCodeFF = 0xff;
        final int maskCodeFF00 = 0xff00;
        final int shiftBit8 = 8;
        final int shiftBit24 = 24;
        
        int targets = (byte1 & maskCodeFF) | ((byte2 << shiftBit8) & maskCodeFF00) // | 表示安位或
                | ((byte3 << shiftBit24) >>> shiftBit8) | (byte4 << shiftBit24);
        return targets;
    }

    /**
     * 解析BCD格式的时间
     * 
     * @param bcdDate
     * @return
     */
    public String bytesToBCDTimeString(byte[] bcdDate) {
        final int dayByteAddr = 3;
        final int hourByteAddr = 4;
        final int minuteByteAddr = 5;
        final int secondByteAddr = 6;
        
        String year1 = Integer.toHexString(bcdDate[0]);
        String year2 = Integer.toHexString(bcdDate[1]);
        String month = Integer.toHexString(bcdDate[2]);
        String day = Integer.toHexString(bcdDate[dayByteAddr]);
        String hour = Integer.toHexString(bcdDate[hourByteAddr]);
        String minute = Integer.toHexString(bcdDate[minuteByteAddr]);
        String second = Integer.toHexString(bcdDate[secondByteAddr]);

        return year1 + year2 + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext arg0, ExceptionEvent arg1) throws Exception {
        // TODO Auto-generated method stub
        super.exceptionCaught(arg0, arg1);
    }

    @Test
    public void test() {

        final byte[] source1 = new byte[] {0x17, 0x20, 0x00, 0x00, (byte) 0xb7, (byte) 0x93, 0x20, 0x09, 0x7e, 0x00, 0x00,
                                           0x00, 0x01, 0x01, 0x01, 0x01, 0x10, (byte) 0x9e, 0x40, 0x01, 0x20, 0x18, 0x01, 0x17, 0x09, 0x16, 0x33,
                                           0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x32, 0x42, 0x00, 0x00, 0x00, 0x00,
                                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                           0x00, 0x00, 0x00 };
        BdDoorAccessModel model = decode(source1);
        System.out.println(model);
    }

}
